@@ -1,5 +1,15 @@
Revision history for Log-Dispatchouli
+2.002 2011-01-14 17:51:16 America/New_York
+ add env_value and env_prefix methods
+
+2.001 2011-01-13 12:45:05 America/New_York
+ greatly expanded tests and documentation for L::D::Global
+
+ L::D now has "string_flogger" method to provide an alternate flogger
+
+2.000 2010-11-23 18:48:53 America/New_York
+
1.102350 2010-08-23 08:55:17 America/New_York
fix a bug in generating tempdirs for file logging (thanks, Sawyer X)
@@ -1,4 +1,4 @@
-This software is copyright (c) 2010 by Ricardo SIGNES.
+This software is copyright (c) 2011 by Ricardo SIGNES.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -12,7 +12,7 @@ b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
-This software is Copyright (c) 2010 by Ricardo SIGNES.
+This software is Copyright (c) 2011 by Ricardo SIGNES.
This is free software, licensed under:
@@ -270,7 +270,7 @@ That's all there is to it!
--- The Artistic License 1.0 ---
-This software is Copyright (c) 2010 by Ricardo SIGNES.
+This software is Copyright (c) 2011 by Ricardo SIGNES.
This is free software, licensed under:
@@ -7,7 +7,15 @@ Makefile.PL
README
dist.ini
lib/Log/Dispatchouli.pm
+lib/Log/Dispatchouli/Global.pm
lib/Log/Dispatchouli/Proxy.pm
t/basic.t
+t/env-value.t
+t/global-subclass.t
+t/global.t
+t/lib/DDR/Child.pm
+t/lib/DDR/Parent.pm
+t/lib/SDR/Child.pm
+t/lib/SDR/Parent.pm
t/proxy.t
t/release-pod-syntax.t
@@ -4,7 +4,7 @@
"Ricardo SIGNES <rjbs@cpan.org>"
],
"dynamic_config" : 0,
- "generated_by" : "Dist::Zilla version 4.102220, CPAN::Meta::Converter version 2.102160",
+ "generated_by" : "Dist::Zilla version 4.200000, CPAN::Meta::Converter version 2.102400",
"license" : [
"perl_5"
],
@@ -22,6 +22,7 @@
"runtime" : {
"requires" : {
"Carp" : 0,
+ "File::Spec" : 0,
"Log::Dispatch" : 0,
"Log::Dispatch::Array" : 0,
"Log::Dispatch::File" : 0,
@@ -30,6 +31,8 @@
"Params::Util" : 0,
"Scalar::Util" : 0,
"String::Flogger" : 0,
+ "Sub::Exporter" : 0,
+ "Sub::Exporter::GlobExporter" : "0.002",
"Sys::Syslog" : "0.16",
"Try::Tiny" : "0.04",
"overload" : 0
@@ -38,7 +41,8 @@
"test" : {
"requires" : {
"Test::Deep" : 0,
- "Test::More" : "0.88"
+ "Test::Fatal" : 0,
+ "Test::More" : "0.96"
}
}
},
@@ -49,174 +53,185 @@
"url" : "git://git.codesimply.com/Log-Dispatchouli.git"
}
},
- "version" : "1.102350",
+ "version" : "2.002",
"x_Dist_Zilla" : {
"plugins" : [
{
"class" : "Dist::Zilla::Plugin::GatherDir",
"name" : "@RJBS/@Basic/GatherDir",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::PruneCruft",
"name" : "@RJBS/@Basic/PruneCruft",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::ManifestSkip",
"name" : "@RJBS/@Basic/ManifestSkip",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::MetaYAML",
"name" : "@RJBS/@Basic/MetaYAML",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::License",
"name" : "@RJBS/@Basic/License",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::Readme",
"name" : "@RJBS/@Basic/Readme",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::ExtraTests",
"name" : "@RJBS/@Basic/ExtraTests",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::ExecDir",
"name" : "@RJBS/@Basic/ExecDir",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::ShareDir",
"name" : "@RJBS/@Basic/ShareDir",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::MakeMaker",
"name" : "@RJBS/@Basic/MakeMaker",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::Manifest",
"name" : "@RJBS/@Basic/Manifest",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::TestRelease",
"name" : "@RJBS/@Basic/TestRelease",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::ConfirmRelease",
"name" : "@RJBS/@Basic/ConfirmRelease",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::UploadToCPAN",
"name" : "@RJBS/@Basic/UploadToCPAN",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
- "class" : "Dist::Zilla::Plugin::AutoPrereq",
- "name" : "@RJBS/AutoPrereq",
- "version" : "4.102220"
+ "class" : "Dist::Zilla::Plugin::AutoPrereqs",
+ "name" : "@RJBS/AutoPrereqs",
+ "version" : "4.200000"
},
{
- "class" : "Dist::Zilla::Plugin::AutoVersion",
- "name" : "@RJBS/AutoVersion",
- "version" : "4.102220"
+ "class" : "Dist::Zilla::Plugin::Git::NextVersion",
+ "name" : "@RJBS/Git::NextVersion",
+ "version" : "1.103520"
},
{
"class" : "Dist::Zilla::Plugin::PkgVersion",
"name" : "@RJBS/PkgVersion",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::MetaConfig",
"name" : "@RJBS/MetaConfig",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::MetaJSON",
"name" : "@RJBS/MetaJSON",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::NextRelease",
"name" : "@RJBS/NextRelease",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::PodSyntaxTests",
"name" : "@RJBS/PodSyntaxTests",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::Repository",
"name" : "@RJBS/Repository",
- "version" : "0.13"
+ "version" : "0.17"
+ },
+ {
+ "class" : "Dist::Zilla::Plugin::Prereqs",
+ "config" : {
+ "Dist::Zilla::Plugin::Prereqs" : {
+ "phase" : "test",
+ "type" : "requires"
+ }
+ },
+ "name" : "@RJBS/TestMoreWithSubtests",
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::PodWeaver",
"name" : "@RJBS/PodWeaver",
- "version" : "3.101640"
+ "version" : "3.101641"
},
{
"class" : "Dist::Zilla::Plugin::Git::Check",
"name" : "@RJBS/@Git/Check",
- "version" : "1.102090"
+ "version" : "1.103520"
},
{
"class" : "Dist::Zilla::Plugin::Git::Commit",
"name" : "@RJBS/@Git/Commit",
- "version" : "1.102090"
+ "version" : "1.103520"
},
{
"class" : "Dist::Zilla::Plugin::Git::Tag",
"name" : "@RJBS/@Git/Tag",
- "version" : "1.102090"
+ "version" : "1.103520"
},
{
"class" : "Dist::Zilla::Plugin::Git::Push",
"name" : "@RJBS/@Git/Push",
- "version" : "1.102090"
+ "version" : "1.103520"
},
{
- "class" : "Dist::Zilla::Plugin::Prereq",
+ "class" : "Dist::Zilla::Plugin::Prereqs",
"config" : {
"Dist::Zilla::Plugin::Prereqs" : {
"phase" : "runtime",
"type" : "requires"
}
},
- "name" : "Prereq",
- "version" : "4.102220"
+ "name" : "Prereqs",
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":InstallModules",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":TestFiles",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":ExecFiles",
- "version" : "4.102220"
+ "version" : "4.200000"
},
{
"class" : "Dist::Zilla::Plugin::FinderCode",
"name" : ":ShareFiles",
- "version" : "4.102220"
+ "version" : "4.200000"
}
],
"zilla" : {
@@ -224,7 +239,7 @@
"config" : {
"is_trial" : 0
},
- "version" : "4.102220"
+ "version" : "4.200000"
}
}
}
@@ -4,11 +4,12 @@ author:
- 'Ricardo SIGNES <rjbs@cpan.org>'
build_requires:
Test::Deep: 0
- Test::More: 0.88
+ Test::Fatal: 0
+ Test::More: 0.96
configure_requires:
ExtUtils::MakeMaker: 6.31
dynamic_config: 0
-generated_by: 'Dist::Zilla version 4.102220, CPAN::Meta::Converter version 2.102160'
+generated_by: 'Dist::Zilla version 4.200000, CPAN::Meta::Converter version 2.102400'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
@@ -16,6 +17,7 @@ meta-spec:
name: Log-Dispatchouli
requires:
Carp: 0
+ File::Spec: 0
Log::Dispatch: 0
Log::Dispatch::Array: 0
Log::Dispatch::File: 0
@@ -24,148 +26,158 @@ requires:
Params::Util: 0
Scalar::Util: 0
String::Flogger: 0
+ Sub::Exporter: 0
+ Sub::Exporter::GlobExporter: 0.002
Sys::Syslog: 0.16
Try::Tiny: 0.04
overload: 0
resources:
repository: git://git.codesimply.com/Log-Dispatchouli.git
-version: 1.102350
+version: 2.002
x_Dist_Zilla:
plugins:
-
class: Dist::Zilla::Plugin::GatherDir
name: '@RJBS/@Basic/GatherDir'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::PruneCruft
name: '@RJBS/@Basic/PruneCruft'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::ManifestSkip
name: '@RJBS/@Basic/ManifestSkip'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::MetaYAML
name: '@RJBS/@Basic/MetaYAML'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::License
name: '@RJBS/@Basic/License'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::Readme
name: '@RJBS/@Basic/Readme'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::ExtraTests
name: '@RJBS/@Basic/ExtraTests'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::ExecDir
name: '@RJBS/@Basic/ExecDir'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::ShareDir
name: '@RJBS/@Basic/ShareDir'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::MakeMaker
name: '@RJBS/@Basic/MakeMaker'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::Manifest
name: '@RJBS/@Basic/Manifest'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::TestRelease
name: '@RJBS/@Basic/TestRelease'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::ConfirmRelease
name: '@RJBS/@Basic/ConfirmRelease'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::UploadToCPAN
name: '@RJBS/@Basic/UploadToCPAN'
- version: 4.102220
+ version: 4.200000
-
- class: Dist::Zilla::Plugin::AutoPrereq
- name: '@RJBS/AutoPrereq'
- version: 4.102220
+ class: Dist::Zilla::Plugin::AutoPrereqs
+ name: '@RJBS/AutoPrereqs'
+ version: 4.200000
-
- class: Dist::Zilla::Plugin::AutoVersion
- name: '@RJBS/AutoVersion'
- version: 4.102220
+ class: Dist::Zilla::Plugin::Git::NextVersion
+ name: '@RJBS/Git::NextVersion'
+ version: 1.103520
-
class: Dist::Zilla::Plugin::PkgVersion
name: '@RJBS/PkgVersion'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::MetaConfig
name: '@RJBS/MetaConfig'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::MetaJSON
name: '@RJBS/MetaJSON'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::NextRelease
name: '@RJBS/NextRelease'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::PodSyntaxTests
name: '@RJBS/PodSyntaxTests'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::Repository
name: '@RJBS/Repository'
- version: 0.13
+ version: 0.17
+ -
+ class: Dist::Zilla::Plugin::Prereqs
+ config:
+ Dist::Zilla::Plugin::Prereqs:
+ phase: test
+ type: requires
+ name: '@RJBS/TestMoreWithSubtests'
+ version: 4.200000
-
class: Dist::Zilla::Plugin::PodWeaver
name: '@RJBS/PodWeaver'
- version: 3.101640
+ version: 3.101641
-
class: Dist::Zilla::Plugin::Git::Check
name: '@RJBS/@Git/Check'
- version: 1.102090
+ version: 1.103520
-
class: Dist::Zilla::Plugin::Git::Commit
name: '@RJBS/@Git/Commit'
- version: 1.102090
+ version: 1.103520
-
class: Dist::Zilla::Plugin::Git::Tag
name: '@RJBS/@Git/Tag'
- version: 1.102090
+ version: 1.103520
-
class: Dist::Zilla::Plugin::Git::Push
name: '@RJBS/@Git/Push'
- version: 1.102090
+ version: 1.103520
-
- class: Dist::Zilla::Plugin::Prereq
+ class: Dist::Zilla::Plugin::Prereqs
config:
Dist::Zilla::Plugin::Prereqs:
phase: runtime
type: requires
- name: Prereq
- version: 4.102220
+ name: Prereqs
+ version: 4.200000
-
class: Dist::Zilla::Plugin::FinderCode
name: ':InstallModules'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::FinderCode
name: ':TestFiles'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::FinderCode
name: ':ExecFiles'
- version: 4.102220
+ version: 4.200000
-
class: Dist::Zilla::Plugin::FinderCode
name: ':ShareFiles'
- version: 4.102220
+ version: 4.200000
zilla:
class: Dist::Zilla::Dist::Builder
config:
is_trial: 0
- version: 4.102220
+ version: 4.200000
@@ -13,7 +13,8 @@ my %WriteMakefileArgs = (
'AUTHOR' => 'Ricardo SIGNES <rjbs@cpan.org>',
'BUILD_REQUIRES' => {
'Test::Deep' => '0',
- 'Test::More' => '0.88'
+ 'Test::Fatal' => '0',
+ 'Test::More' => '0.96'
},
'CONFIGURE_REQUIRES' => {
'ExtUtils::MakeMaker' => '6.31'
@@ -24,6 +25,7 @@ my %WriteMakefileArgs = (
'NAME' => 'Log::Dispatchouli',
'PREREQ_PM' => {
'Carp' => '0',
+ 'File::Spec' => '0',
'Log::Dispatch' => '0',
'Log::Dispatch::Array' => '0',
'Log::Dispatch::File' => '0',
@@ -32,11 +34,13 @@ my %WriteMakefileArgs = (
'Params::Util' => '0',
'Scalar::Util' => '0',
'String::Flogger' => '0',
+ 'Sub::Exporter' => '0',
+ 'Sub::Exporter::GlobExporter' => '0.002',
'Sys::Syslog' => '0.16',
'Try::Tiny' => '0.04',
'overload' => '0'
},
- 'VERSION' => '1.102350',
+ 'VERSION' => '2.002',
'test' => {
'TESTS' => 't/*.t'
}
@@ -1,11 +1,11 @@
This archive contains the distribution Log-Dispatchouli,
-version 1.102350:
+version 2.002:
a simple wrapper around Log::Dispatch
-This software is copyright (c) 2010 by Ricardo SIGNES.
+This software is copyright (c) 2011 by Ricardo SIGNES.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -4,7 +4,6 @@ license = Perl_5
copyright_holder = Ricardo SIGNES
[@RJBS]
-version = 1
-[Prereq]
+[Prereqs]
Sys::Syslog = 0.16 ; native socket type
@@ -0,0 +1,278 @@
+use strict;
+use warnings;
+package Log::Dispatchouli::Global;
+BEGIN {
+ $Log::Dispatchouli::Global::VERSION = '2.002';
+}
+# ABSTRACT: a system for sharing a global, dynamically-scoped logger
+
+use Carp ();
+use Log::Dispatchouli;
+use Scalar::Util ();
+
+use Sub::Exporter::GlobExporter 0.002 qw(glob_exporter); # pass-through args
+use Sub::Exporter -setup => {
+ collectors => {
+ '$Logger' => glob_exporter(Logger => \'_build_logger'),
+ },
+};
+
+
+sub logger_globref {
+ no warnings 'once';
+ \*Logger;
+}
+
+sub current_logger {
+ my ($self) = @_;
+
+ my $globref = $self->logger_globref;
+
+ unless (defined $$$globref) {
+ $$$globref = $self->default_logger;
+ }
+
+ return $$$globref;
+}
+
+
+sub default_logger {
+ my ($self) = @_;
+
+ my $ref = $self->default_logger_ref;
+
+ $$ref ||= $self->default_logger_class->new(
+ $self->default_logger_args
+ );
+}
+
+
+sub default_logger_class { 'Log::Dispatchouli' }
+
+
+sub default_logger_args {
+ return {
+ ident => "default/$0",
+ facility => undef,
+ }
+}
+
+
+my %default_logger_for_glob;
+
+sub default_logger_ref {
+ my ($self) = @_;
+
+ my $glob = $self->logger_globref;
+ my $addr = Scalar::Util::refaddr($glob);
+ return \$default_logger_for_glob{ $addr };
+}
+
+sub _build_logger {
+ my ($self, $arg) = @_;
+
+ my $globref = $self->logger_globref;
+ my $default = $self->default_logger;
+
+ my $Logger = $$$globref;
+
+ if ($arg and $arg->{init}) {
+ if (
+ $Logger
+ and
+ Scalar::Util::refaddr($Logger) != Scalar::Util::refaddr($default)
+ ) {
+ Carp::confess("attempted to initialize $self logger twice");
+ }
+
+ $$$globref = $self->default_logger_class->new($arg->{init});
+ } else {
+ $$$globref ||= $default;
+ }
+
+ return $globref;
+}
+
+
+1;
+
+__END__
+=pod
+
+=head1 NAME
+
+Log::Dispatchouli::Global - a system for sharing a global, dynamically-scoped logger
+
+=head1 VERSION
+
+version 2.002
+
+=head1 DESCRIPTION
+
+B<Warning>: This interface is still experimental.
+
+Log::Dispatchouli::Global is a framework for a global logger object. In your
+top-level programs that are actually executed, you'd add something like this:
+
+ use Log::Dispatchouli::Global '$Logger' => {
+ init => {
+ ident => 'My::Daemon',
+ facility => 'local2',
+ to_stdout => 1,
+ },
+ };
+
+This will import a C<$Logger> into your program, and more importantly will
+initialize it with a new L<Log::Dispatchouli> object created by passing the
+value for the C<init> parameter to Log::Dispatchouli's C<new> method.
+
+Much of the rest of your program, across various libraries, can then just use
+this:
+
+ use Log::Dispatchouli::Global '$Logger';
+
+ sub whatever {
+ ...
+
+ $Logger->log("about to do something");
+
+ local $Logger = $Logger->proxy({ proxy_prefix => "whatever: " });
+
+ for (@things) {
+ $Logger->log([ "doing thing %s", $_ ]);
+ ...
+ }
+ }
+
+This eliminates the need to pass around what is effectively a global, while
+still allowing it to be specialized withing certain contexts of your program.
+
+B<Warning!> Although you I<could> just use Log::Dispatchouli::Global as your
+shared logging library, you almost I<certainly> want to write a subclass that
+will only be shared amongst your application's classes.
+Log::Dispatchouli::Global is meant to be subclassed and shared only within
+controlled systems. Remember, I<sharing your state with code you don't
+control is dangerous>.
+
+=head1 USING
+
+In general, you will either be using a Log::Dispatchouli::Global class to get
+a C<$Logger> or to initialize it (and then get C<$Logger>). These are both
+demonstrated above. Also, when importing C<$Logger> you may request it be
+imported under a different name:
+
+ use Log::Dispatchouli::Global '$Logger' => { -as => 'L' };
+
+ $L->log( ... );
+
+There is only one class method that you are likely to use: C<current_logger>.
+This provides the value of the shared logger from the caller's context,
+initializing it to a default if needed. Even this method is unlikely to be
+required frequently, but it I<does> allow users to I<see> C<$Logger> without
+importing it.
+
+=head1 SUBCLASSING
+
+Before using Log::Dispatchouli::Global in your application, you should subclass
+it. When you subclass it, you should provide the following methods:
+
+=head2 logger_globref
+
+This method should return a globref in which the shared logger will be stored.
+Subclasses will be in their own package, so barring any need for cleverness,
+every implementation of this method can look like the following:
+
+ sub logger_globref { no warnings 'once'; return \*Logger }
+
+=head2 default_logger
+
+If no logger has been initialized, but something tries to log, it gets the
+default logger, created by calling this method.
+
+The default implementation calls C<new> on the C<default_logger_class> with the
+result of C<default_logger_args> as the arguments.
+
+=head2 default_logger_class
+
+This returns the class on which C<new> will be called when initializing a
+logger, either from the C<init> argument when importing or the default logger.
+
+Its default value is Log::Dispatchouli.
+
+=head2 default_logger_args
+
+If no logger has been initialized, but something tries to log, it gets the
+default logger, created by calling C<new> on the C<default_logger_class> and
+passing the results of calling this method.
+
+Its default return value creates a sink, so that anything logged without an
+initialized logger is lost.
+
+=head2 default_logger_ref
+
+This method returns a scalar reference in which the cached default value is
+stored for comparison. This is used when someone tries to C<init> the global.
+When someone tries to initialize the global logger, and it's already set, then:
+
+=over 4
+
+=item *
+
+if the current value is the same as the default, the new value is set
+
+=item *
+
+if the current value is I<not> the same as the default, we die
+
+=back
+
+Since you want the default to be isolated to your application's logger, the
+default behavior is default loggers are associated with the glob reference to
+which the default might be assigned. It is recommended that you replace this
+method to return a shared, private variable for your subclasses, by putting the
+following code in the base class for your Log::Dispatchouli::Global classes:
+
+ my $default_logger;
+ sub default_logger_ref { \$default_logger };
+
+=head1 COOKBOOK
+
+=head2 Common Logger Recipes
+
+Say you often use the same configuration for one kind of program, like
+automated tests. You've already written your own subclass to get your own
+storage and defaults, maybe C<MyApp::Logger>.
+
+You can't just write a subclass with a different default, because if another
+class using the same global has set the global with I<its> default, yours won't
+be honored. You don't just want this new value to be the default, you want it
+to be I<the> logger. What you want to do in this case is to initialize your
+logger normally, then reexport it, like this:
+
+ package MyApp::Logger::Test;
+ use parent 'MyApp::Logger';
+
+ use MyApp::Logger '$Logger' => {
+ init => {
+ ident => "Tester($0)",
+ to_self => 1,
+ facility => undef,
+ },
+ };
+
+This will set up the logger and re-export it, and will properly die if anything
+else attempts to initialize the logger to something else.
+
+=head1 AUTHOR
+
+Ricardo SIGNES <rjbs@cpan.org>
+
+=head1 COPYRIGHT AND LICENSE
+
+This software is copyright (c) 2011 by Ricardo SIGNES.
+
+This is free software; you can redistribute it and/or modify it under
+the same terms as the Perl 5 programming language system itself.
+
+=cut
+
@@ -2,7 +2,7 @@ use strict;
use warnings;
package Log::Dispatchouli::Proxy;
BEGIN {
- $Log::Dispatchouli::Proxy::VERSION = '1.102350';
+ $Log::Dispatchouli::Proxy::VERSION = '2.002';
}
# ABSTRACT: a simple wrapper around Log::Dispatch
@@ -38,6 +38,8 @@ sub proxy {
sub parent { $_[0]{parent} }
sub logger { $_[0]{logger} }
+sub ident { $_[0]{logger}->ident }
+
sub set_prefix { $_[0]{prefix} = $_[1] }
sub get_prefix { $_[0]{prefix} }
sub clear_prefix { undef $_[0]{prefix} }
@@ -117,7 +119,7 @@ Log::Dispatchouli::Proxy - a simple wrapper around Log::Dispatch
=head1 VERSION
-version 1.102350
+version 2.002
=head1 DESCRIPTION
@@ -150,7 +152,7 @@ Ricardo SIGNES <rjbs@cpan.org>
=head1 COPYRIGHT AND LICENSE
-This software is copyright (c) 2010 by Ricardo SIGNES.
+This software is copyright (c) 2011 by Ricardo SIGNES.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -2,11 +2,12 @@ use strict;
use warnings;
package Log::Dispatchouli;
BEGIN {
- $Log::Dispatchouli::VERSION = '1.102350';
+ $Log::Dispatchouli::VERSION = '2.002';
}
# ABSTRACT: a simple wrapper around Log::Dispatch
use Carp ();
+use File::Spec ();
use Log::Dispatch;
use Params::Util qw(_ARRAY0 _HASH0 _CODELIKE);
use Scalar::Util qw(blessed weaken);
@@ -43,7 +44,7 @@ sub new {
if ($arg->{to_file}) {
require Log::Dispatch::File;
my $log_file = File::Spec->catfile(
- ($ENV{DISPATCHOULI_PATH} || File::Spec->tmpdir),
+ ($self->env_value('PATH') || File::Spec->tmpdir),
sprintf('%s.%04u%02u%02u',
$ident,
((localtime)[5] + 1900),
@@ -67,7 +68,7 @@ sub new {
);
}
- if ($arg->{facility} and not $ENV{DISPATCHOULI_NOSYSLOG}) {
+ if ($arg->{facility} and not $self->env_value('NOSYSLOG')) {
require Log::Dispatch::Syslog;
$log->add(
Log::Dispatch::Syslog->new(
@@ -115,10 +116,11 @@ sub new {
$self->{dispatcher} = $log;
$self->{prefix} = $arg->{prefix};
+ $self->{ident} = $ident;
$self->{debug} = exists $arg->{debug}
? ($arg->{debug} ? 1 : 0)
- : ($ENV{DISPATCHOULI_DEBUG} ? 1 : 0);
+ : ($self->env_value('DEBUG') ? 1 : 0);
$self->{fail_fatal} = exists $arg->{fail_fatal} ? $arg->{fail_fatal} : 1;
@@ -136,7 +138,8 @@ sub log {
if ($arg->{fatal} or ! $self->get_muted) {
try {
- my @flogged = map {; String::Flogger->flog($_) } @rest;
+ my $flogger = $self->string_flogger;
+ my @flogged = map {; $flogger->flog($_) } @rest;
$message = @flogged > 1 ? $self->_join(\@flogged) : $flogged[0];
my $prefix = _ARRAY0($arg->{prefix})
@@ -223,6 +226,30 @@ sub clear_prefix { $_[0]->unset_prefix }
sub unset_prefix { undef $_[0]->{prefix} }
+sub ident { $_[0]{ident} }
+
+
+
+sub string_flogger { 'String::Flogger' }
+
+
+sub env_prefix { return; }
+
+
+sub env_value {
+ my ($self, $suffix) = @_;
+
+ my @path = grep { defined } ($self->env_prefix, 'DISPATCHOULI');
+
+ for my $prefix (@path) {
+ my $name = join q{_}, $prefix, $suffix;
+ return $ENV{ $name } if defined $ENV{ $name };
+ }
+
+ return;
+}
+
+
sub new_tester {
my ($class, $arg) = @_;
$arg ||= {};
@@ -303,7 +330,7 @@ Log::Dispatchouli - a simple wrapper around Log::Dispatch
=head1 VERSION
-version 1.102350
+version 2.002
=head1 SYNOPSIS
@@ -452,6 +479,10 @@ This method changes the prefix. See L<Logger Prefix|/LOGGER PREFIX>.
This method clears any set logger prefix. (It can also be called as
C<unset_prefix>, but this is deprecated. See L<Logger Prefix|/LOGGER PREFIX>.
+=head2 ident
+
+This method returns the logger's ident.
+
=head2 dispatcher
This returns the underlying Log::Dispatch object. This is not the method
@@ -500,6 +531,32 @@ settings, which accumulate. So:
Batch 123: Subsystem 12: Page 9: Paragraph 6: Done.
+=head1 METHODS FOR SUBCLASSING
+
+=head2 string_flogger
+
+This method returns the thing on which F<flog> will be called to format log
+messages. By default, it just returns C<String::Flogger>
+
+=head2 env_prefix
+
+This method should return a string used as a prefix to find environment
+variables that affect the logger's behavior. For example, if this method
+returns C<XYZZY> then when checking the environment for a default value for the
+C<debug> parameter, Log::Dispatchouli will first check C<XYZZY_DEBUG>, then
+C<DISPATCHOULI_DEBUG>.
+
+By default, this method returns C<()>, which means no extra environment
+variable is checked.
+
+=head2 env_value
+
+ my $value = $logger->env_value('DEBUG');
+
+This method returns the value for the environment variable suffix given. For
+example, the example given, calling with C<DEBUG> will check
+C<DISPATCHOULI_DEBUG>.
+
=head1 METHODS FOR TESTING
=head2 new_tester
@@ -605,7 +662,7 @@ Ricardo SIGNES <rjbs@cpan.org>
=head1 COPYRIGHT AND LICENSE
-This software is copyright (c) 2010 by Ricardo SIGNES.
+This software is copyright (c) 2011 by Ricardo SIGNES.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
@@ -6,10 +6,15 @@ use Test::More 0.88;
use Test::Deep;
{
- my $logger = Log::Dispatchouli->new_tester({ log_pid => 1 });
+ my $logger = Log::Dispatchouli->new_tester({
+ log_pid => 1,
+ ident => 't/basic.t',
+ });
isa_ok($logger, 'Log::Dispatchouli');
+ is($logger->ident, 't/basic.t', '$logger->ident is available');
+
$logger->log([ "point: %s", {x=>1,y=>2} ]);
$logger->log_debug('this will not get logged');
@@ -0,0 +1,44 @@
+#!perl
+use strict;
+use warnings;
+
+use Test::More;
+
+use Log::Dispatchouli;
+
+{
+ package Xyzzy::Logger;
+ use base 'Log::Dispatchouli';
+
+ sub env_prefix { 'XYZZY' }
+}
+
+{
+ local $ENV{DISPATCHOULI_DEBUG} = 1;
+ local $ENV{XYZZY_DEBUG} = 0;
+ my $d_logger = Log::Dispatchouli->new_tester;
+ my $x_logger = Xyzzy::Logger->new_tester;
+
+ ok( $d_logger->is_debug, "DISPATCHOULI_ affects L::D logger");
+ ok( ! $x_logger->is_debug, "...but XYZZY_ overrides for X::L");
+}
+
+{
+ local $ENV{DISPATCHOULI_DEBUG} = 1;
+ my $d_logger = Log::Dispatchouli->new_tester;
+ my $x_logger = Xyzzy::Logger->new_tester;
+
+ ok( $d_logger->is_debug, "DISPATCHOULI_ affects L::D logger");
+ ok( $x_logger->is_debug, "...and X::L will use it with no XYZZY_");
+}
+
+{
+ local $ENV{XYZZY_DEBUG} = 1;
+ my $d_logger = Log::Dispatchouli->new_tester;
+ my $x_logger = Xyzzy::Logger->new_tester;
+
+ ok( $x_logger->is_debug, "XYZZY_ affects X::L");
+ ok( ! $d_logger->is_debug, "...but not L::D");
+}
+
+done_testing;
@@ -0,0 +1,40 @@
+use strict;
+use warnings;
+use Test::More;
+
+use Scalar::Util qw(refaddr);
+
+use lib 't/lib';
+
+# DDR - default default ref -- uses the default default_logger_ref
+{ package DDR_P; use DDR::Parent '$Logger'; }
+{ package DDR_C; use DDR::Child '$Logger'; }
+
+# SDR - shared default ref -- uses a default_logger_ref shared between classes
+{ package SDR_P; use SDR::Parent '$Logger'; }
+{ package SDR_C; use SDR::Child '$Logger'; }
+
+is(
+ refaddr( $DDR_P::Logger ),
+ refaddr( $DDR_C::Logger ),
+ "DDR parent and child share logger storage",
+);
+
+# DDR::Child can store its default in a different place, but
+# $DDR::Parent::Logger is already defined when we get here, so the logic is
+# "already defined and not equal to *my* default, so it is untouched."
+is($DDR_P::Logger->ident, 'DDR::Parent', "parent won the initialization race");
+
+is(
+ refaddr( $SDR_P::Logger ),
+ refaddr( $SDR_C::Logger ),
+ "SDR parent and child share logger storage",
+);
+
+is(
+ $SDR_P::Logger->ident,
+ 'SDR::Parent',
+ "SDR::Parent is initialized first, so its default wins",
+);
+
+done_testing;
@@ -0,0 +1,54 @@
+use strict;
+use warnings;
+
+use Test::Fatal;
+use Test::More;
+
+use Log::Dispatchouli::Global '$Logger' => { init => {
+ ident => 't/global.t',
+ log_pid => 0,
+ to_self => 1,
+}};
+
+{
+ package Alpha;
+ use Log::Dispatchouli::Global '$Logger';
+
+ sub call_bravo {
+ my ($self, $n) = @_;
+
+ local $Logger = $Logger->proxy({ proxy_prefix => "$n: " });
+
+ $Logger->log("inside call_bravo");
+
+ Bravo->endpoint;
+ }
+}
+
+{
+ package Bravo;
+ use Log::Dispatchouli::Global '$Logger' => { -as => 'L' };
+
+ sub endpoint {
+ my ($self, $n) = @_;
+
+ $L->log("inside Bravo::endpoint");
+ }
+}
+
+isa_ok($Logger, 'Log::Dispatchouli', 'imported $Logger');
+
+$Logger->log("first");
+
+Alpha->call_bravo(123);
+
+$Logger->log("last");
+
+my $events = $Logger->events;
+
+is($events->[0]->{message}, 'first', '1st log');
+is($events->[1]->{message}, '123: inside call_bravo', '2nd log');
+is($events->[2]->{message}, '123: inside Bravo::endpoint', '3rd log');
+is($events->[3]->{message}, 'last', '4th log');
+
+done_testing;
@@ -0,0 +1,11 @@
+use strict;
+package DDR::Child;
+use base 'DDR::Parent';
+sub default_logger_args {
+ return {
+ ident => __PACKAGE__,
+ log_pid => 0,
+ to_self => 1,
+ }
+}
+1;
@@ -0,0 +1,12 @@
+use strict;
+package DDR::Parent;
+use base 'Log::Dispatchouli::Global';
+sub logger_globref { no warnings 'once'; \*Logger }
+sub default_logger_args {
+ return {
+ ident => __PACKAGE__,
+ log_pid => 0,
+ to_self => 1,
+ }
+}
+1;
@@ -0,0 +1,11 @@
+use strict;
+package SDR::Child;
+use base 'SDR::Parent';
+sub default_logger_args {
+ return {
+ ident => __PACKAGE__,
+ log_pid => 0,
+ to_self => 1,
+ }
+}
+1;
@@ -0,0 +1,15 @@
+use strict;
+package SDR::Parent;
+use base 'Log::Dispatchouli::Global';
+sub logger_globref { no warnings 'once'; \*Logger }
+
+my $default_logger;
+sub default_logger_ref { \$default_logger }
+sub default_logger_args {
+ return {
+ ident => __PACKAGE__,
+ log_pid => 0,
+ to_self => 1,
+ }
+}
+1;
@@ -4,7 +4,9 @@ use warnings;
use Log::Dispatchouli;
use Test::More 0.88;
-my $logger = Log::Dispatchouli->new_tester;
+my $logger = Log::Dispatchouli->new_tester({
+ ident => 't/proxy.t',
+});
sub are_events {
my ($comment, $want) = @_;
@@ -17,6 +19,8 @@ sub are_events {
$logger->log("1");
+is($logger->ident, 't/proxy.t', '$logger->ident is available');
+
are_events("we can log a simple event", [ '1' ]);
$logger->set_prefix("A: ");
@@ -30,6 +34,8 @@ my $proxy = $logger->proxy({
proxy_prefix => 'B: ',
});
+is($proxy->ident, 't/proxy.t', '$proxy->ident is available');
+
$proxy->log("3");
are_events("log with proxy with prefix", [
@@ -85,7 +91,7 @@ $logger->log_debug("logger debug");
$proxy->log_debug("proxy debug");
$proxprox->log_debug("proxprox debug");
-are_events("debugging in middle tier", [
+are_events("debugging in middle tier (middle set_debug)", [
'A: B: C: proxy debug',
'A: B: C: E: F: proxprox debug',
]);